package gotnames.dm;

import java.util.Date;
import java.util.concurrent.TimeUnit;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import com.medallia.tiny.Func;

/**
 * Each user has at most one {@link Guess} object per other user in the group,
 * which is created the first time the user makes a guess of the name of the
 * other user. It has various statistics on how many guesses have been made and
 * how many were correct as well as the date of the last guess.
 */
@PersistenceCapable
public class Guess {
	
	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Long key;
	
	@Persistent
	private Long userKey;
	
	@Persistent
	private Long correctUser;
	
	@Persistent
	private Date lastGuess;
	
	@Persistent
	private int numGuesses;
	
	@Persistent
	private int numCorrectGuesses;
	
	public Long getKey() {
		return key;
	}

	/** @return the primary key of the user making the guesses */
	public Long getUserKey() {
		return userKey;
	}

	public void setUserKey(Long userKey) {
		this.userKey = userKey;
	}

	/** @return the primary key of the user the guesses are of */
	public Long getCorrectUser() {
		return correctUser;
	}

	public void setCorrectUser(Long correctUser) {
		this.correctUser = correctUser;
	}

	/** @return the date of the last guess */
	public Date getLastGuess() {
		return lastGuess;
	}

	/** @return the number of guesses that have been made */
	public int getNumGuesses() {
		return numGuesses;
	}

	/** @return number of consecutive correct guesses (resets back to 0 on wrong guess) */
	public int getNumCorrectGuesses() {
		return numCorrectGuesses;
	}
	
	/**
	 * Register a guess attempt. Increments {@link #getNumGuesses()}. If the
	 * guess was correct {@link #getNumCorrectGuesses()} is also incremented,
	 * otherwise it is reset back to zero.
	 * 
	 * @param now
	 *            the date the guess was made
	 * @param correctGuess
	 *            true if the guess was correct.
	 */
	public void registerGuess(Date now, boolean correctGuess) {
		this.lastGuess = now;
		this.numGuesses++;
		if (correctGuess)
			numCorrectGuesses++;
		else
			numCorrectGuesses = 0;
	}
	
	/** @return true if a guess is allowed, i.e. enough time has elapsed since the last guess */
	public static boolean guessAllowed(Date now, Guess guess) {
		return enoughTimeElapsed(daysBetween(guess.getLastGuess(), now), guess.getNumCorrectGuesses());
	}
	
	private static boolean enoughTimeElapsed(int daysBetween, int numCorrectGuesses) {
		if (numCorrectGuesses == 0)
			return daysBetween >= 2;
			
		if (numCorrectGuesses == 1)
			return daysBetween >= 7;
		
		return daysBetween >= 30 * (numCorrectGuesses - 1);
	}

	private static int daysBetween(Date d1, Date d2) {
		return (int) TimeUnit.MILLISECONDS.toDays(d2.getTime() - d1.getTime());
	}

	public static final Func<Guess, Long> CORRECT_USER_FUNC = new Func<Guess, Long>() {
		@Override public Long call(Guess g) {
			return g.getCorrectUser();
		}
	};
	
}
